﻿#pragma once
#include <string>
#include <functional>
#include <memory>
#include <map>
#include <NonCopyable.hpp>

template<typename T>
class ObjectPool : NonCopyable
{
	template<typename... Args>
	using Constructor = std::function<std::shared_ptr<T>(Args...)>;

	using TFunc = std::function<bool(T*)>;
public:
	ObjectPool() = default;
	~ObjectPool() {
		// add by lzhkui 2019.11.28
		m_stop = true;
	}
	//默认创建多少个对象
	template<typename... Args>
	void Init(size_t num, TFunc func, Args&&... args)
	{
		if (num<= 0)
			throw std::logic_error("object num out of range.");
		auto constructName = typeid(Constructor<std::remove_cvref_t<Args>...>).name(); //不区分引用

		_create_obj = [=] {
			// add by lzhkui 2020.04.09 
			auto delete_func = [this, constructName, num](auto* p) {
				auto delete_ptr = [this, constructName, num](auto* p, auto delete_func)
				{
					//删除器中不直接删除对象，而是回收到对象池中，以供下次使用
					// add by lzhkui 2019.11.28
					if (m_stop) {
						delete p;
						p = nullptr;
						return;
					}
					if (m_object_map.size() > 2 * num) {
						// 防止撑爆内存
						delete p;
						p = nullptr;
						return;
					}
					m_object_map.emplace(std::move(constructName), std::shared_ptr<T>(p, [delete_func](auto* p) {return delete_func(p, delete_func); }));
				};
				return delete_ptr(p, delete_ptr);
			};
			T* obj = new T(std::forward<Args>(args)...);
			if (func && !func(obj)) {
				delete obj;
				return std::shared_ptr<T>();
			}
			return std::shared_ptr<T>(obj, delete_func);
		};
		for (size_t i = 0; i < num; i++)
		{
			std::shared_ptr<T> s_t = _create_obj();
			if (!s_t) return;
			m_object_map.emplace(constructName, std::move(s_t));
		}
	}

	//从对象池中获取一个对象
	template<typename... Args>
	std::shared_ptr<T> Get()
	{
		std::string constructName = typeid(Constructor<std::remove_cvref_t<Args>...>).name();

		auto range = m_object_map.equal_range(constructName);
		for (auto it = range.first; it != range.second; ++it)
		{
			auto ptr = it->second;
			m_object_map.erase(it);
			return ptr;
		}
		
		if (std::shared_ptr<T> s_t = _create_obj(); s_t)
			m_object_map.emplace(constructName, std::move(s_t));

		return nullptr;
	}

	size_t size() 
	{
		return m_object_map.size();
	}

	bool empty()
	{
		return m_object_map.empty();
	}

private:

	template <typename ObjectType, typename Base = std::multimap<std::string, std::shared_ptr<ObjectType>>>
	class safeObjectmap {
	public:
		using iterator = typename Base::iterator;
		using key_type = typename Base::key_type;

		template <class... _Valty>
		auto emplace(_Valty&&... _Val) {
			std::scoped_lock<std::mutex> lock(_mt);
			return _object_map.emplace(_STD forward<_Valty>(_Val)...);
		}

		auto size() {
			std::scoped_lock<std::mutex> lock(_mt);
			return _object_map.size();
		}

		bool empty() {
			std::scoped_lock<std::mutex> lock(_mt);
			return _object_map.empty();
		}

		auto equal_range(const key_type& _Keyval) {
			std::scoped_lock<std::mutex> lock(_mt);
			return _object_map.equal_range(_Keyval);
		}

		iterator erase(iterator _Where) {
			std::scoped_lock<std::mutex> lock(_mt);
			return _object_map.erase(_Where);
		}

	private:
		std::mutex _mt;
		Base _object_map;
	};

	safeObjectmap<T> m_object_map;
	bool m_stop{ false }; // add by lzhkui 2019.11.28

	std::function<std::shared_ptr<T>()> _create_obj{ nullptr };
};
